use darling::FromDeriveInput;
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{parse_macro_input, DeriveInput};

use super::{rcc_enable, RccAxB};

#[derive(Debug, FromDeriveInput)]
#[darling(attributes(adc_config))]
struct Attr {
    adc: u8,
    chs: Vec<u8>,
}

pub fn build(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = &input.ident;
    let attr = Attr::from_derive_input(&input).unwrap();

    let rcc_field = format_ident!("adc{}en", attr.adc);
    let rcc = rcc_enable(RccAxB::APB2, &rcc_field, false);
    let adc = format_ident!("ADC{}", attr.adc);
    
    let mut smpr1_smp = Vec::new();
    let mut smpr2_smp = Vec::new();
    let mut sqr3 = Vec::new();
    let mut sqr2 = Vec::new();
    let mut sqr1 = Vec::new();

    for (index, ch) in attr.chs.iter().enumerate() {
        let ch = *ch;
        if ch >= 10 {
            smpr1_smp.push( format_ident!("smp{}", ch) );
        } else {
            smpr2_smp.push( format_ident!("smp{}", ch) );
        }
        let sq = format_ident!("sq{}", index + 1);

        if index <= 5 { // 0-5 => 1-6
            sqr3.push( quote! {
                #sq().variant(#ch)
            } );
        } else if index <= 11 {
            sqr2.push( quote! {
                #sq().variant(#ch)
            } );
        } else {
            sqr1.push( quote! {
                #sq().variant(#ch)
            } );
        }
    }
    let count = attr.chs.len() as u8 - 1;

    sqr1.push(quote! {
        l().variant(#count)
    });
    let mut sqr = quote! {};
    let mut smpr = quote! {};
    if !smpr2_smp.is_empty() {
        smpr.extend(quote! {
            adc.smpr2.write(|w| {
                w #(.#smpr2_smp().cycles239_5())*
            });
        });
    }
    if !smpr1_smp.is_empty() {
        smpr.extend(quote! {
            adc.smpr1.write(|w| {
                w #(.#smpr1_smp().cycles239_5())*
            });
        });
    }

    if !sqr1.is_empty() {
        sqr.extend(quote! {
            adc.sqr1.write(|w| {
                w #(.#sqr1)*
            });
        });
    }
    if !sqr2.is_empty() {
        sqr.extend(quote! {
            adc.sqr2.write(|w| {
                w #(.#sqr2)*
            });
        });
    }
    if !sqr3.is_empty() {
        sqr.extend(quote! {
            adc.sqr3.write(|w| {
                w #(.#sqr3)*
            });
        });
    }

    let expand = quote! {
        impl #name {

            fn adc_start() {
                let adc: stm32f1::stm32f103::#adc = unsafe {
                    ::core::mem::transmute(())
                };
                adc.cr2.modify(|_, w| w.swstart().start());
            }

            fn adc_init() {
                #rcc
                let adc: stm32f1::stm32f103::#adc = unsafe {
                    ::core::mem::transmute(())
                };
                adc.cr2.reset();
                adc.cr1.write(|w| w.scan().enabled());
                adc.cr2.write(|w| {
                    w.cont().continuous()
                    .extsel().swstart()
                    .dma().enabled()
                    .exttrig().enabled()
                });
                #smpr
                #sqr
                adc.cr2.modify(|_, w| w.adon().enabled());
                adc.cr2.modify(|_, w| w.rstcal().initialize());
                while adc.cr2.read().rstcal().is_not_initialized() {}
            
                adc.cr2.modify(|_, w| w.cal().start());
                while adc.cr2.read().cal().is_not_complete() {}

            }
        }    
    };
    expand.into()
}